package com.ivy.parser.logicflow;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.ivy.builder.graph.Node;
import com.ivy.builder.graph.NodeProperties;
import com.ivy.parser.bus.*;
import com.ivy.parser.execption.LiteFlowELException;
import com.ivy.parser.utils.FlowConvertELUtil;
import com.ivy.parser.utils.NodeInfoToELUtil;
import com.yomahub.liteflow.builder.el.*;
import com.yomahub.liteflow.enums.NodeTypeEnum;

import java.util.*;

public class LogicflowExecutor {

    public static ELWrapper elWrapper(LogicFlowGraphEL graphEL) throws LiteFlowELException {
        ELWrapper elWrapper = ELBus.then();
        // 单起点
        if (graphEL.isSingeStart()) {
            Node startNode = graphEL.getStartNode();
            parseSingleFlow(elWrapper, graphEL, startNode, null);
            return graphEL.catchGroupProp(startNode, elWrapper);
        } else {// 多起点
            parseMultipleFlow(elWrapper, graphEL, null, null);
        }
        return elWrapper;
    }

    // 单起点解析
    public static void parseSingleFlow(ELWrapper elWrapper, LogicFlowGraphEL graphEL, Node startNode, Node endNode) throws LiteFlowELException {
        boolean isGroupNodeAndThenELWrapper = graphEL.isGroupNodeAndThenELWrapper(startNode, elWrapper);
         if (isGroupNodeAndThenELWrapper) {
            WhenELWrapper whenELWrapper = ELBus.when();
            ThenELWrapper thenELWrapper = ELBus.then();
            parseSwitchCaseFlow(thenELWrapper, graphEL, startNode, endNode);
            whenELWrapper.when(thenELWrapper);
            graphEL.setGroupNodeProp(startNode, whenELWrapper);
            FlowConvertELUtil.convert(elWrapper, whenELWrapper);
        } else {
            parseSwitchCaseFlow(elWrapper, graphEL, startNode, endNode);
        }
    }

    public static void parseSwitchCaseFlow(ELWrapper elWrapper, LogicFlowGraphEL graphEL, Node startNode, Node endNode) throws LiteFlowELException {
        String nodeType = startNode.getProperties().getNodeType();
        NodeTypeEnum enumByCode = NodeTypeEnum.getEnumByCode(nodeType);
        switch (enumByCode) {
            // 选择组件处理
            case SWITCH:
            case SWITCH_SCRIPT:
                flowSwitch(elWrapper, startNode, endNode, graphEL);
                break;
            case BOOLEAN:
            case BOOLEAN_SCRIPT:
                flowIf(elWrapper, startNode, endNode, graphEL);
                break;
            case FOR:
            case FOR_SCRIPT:
                flowFor(elWrapper, startNode, endNode, graphEL);
                break;
            case ITERATOR:
                flowIterator(elWrapper, startNode, endNode, graphEL);
                break;
            default:
                if (graphEL.isFork(startNode)) {
                    flowFork(elWrapper, startNode, endNode, graphEL);
                    return;
                } else if (graphEL.isJoin(startNode)) {
                    flowThen(elWrapper, startNode);
                } else {
                    flowThen(elWrapper, startNode);
                }
                if (!graphEL.isLastNode(startNode)) {
                    Node nextNode = graphEL.getNextNode(startNode).get(0);
                    if (endNode != null && endNode == nextNode) {
                        return;
                    }
                    parseSingleFlow(elWrapper, graphEL, nextNode, endNode);
                }
                break;
        }
    }

    // 多起点解析
    public static void parseMultipleFlow(ELWrapper wrapper, LogicFlowGraphEL graphEL, List<Node> startNodeList, Node endNode) throws LiteFlowELException {
        if (CollUtil.isEmpty(startNodeList)) {
            startNodeList = graphEL.getStartNodeList();
        }
        Node joinNode = graphEL.getJoinNode(startNodeList);
        flowMultiple(wrapper, startNodeList, joinNode, graphEL);
        if (joinNode != null) {
            parseSingleFlow(wrapper, graphEL, joinNode, endNode);
        }
    }

    // switch处理
    public static void flowSwitch(ELWrapper wrapper, Node startNode, Node endNode, LogicFlowGraphEL graphEL) throws LiteFlowELException {
        List<Node> nodeList = graphEL.getNextNode(startNode);
        List<Node> switchToList = new ArrayList<>();
        List<Node> switchDefaultList = new ArrayList<>();
        List<Node> commonList = new ArrayList<>();
        for (Node node : nodeList) {
            boolean flag1 = graphEL.isCommonLine(startNode, node);
            if (flag1) {
                commonList.add(node);
            }
            boolean flag2 = graphEL.isSwitchToLine(startNode, node);
            if (flag2) {
                switchToList.add(node);
            }
            boolean flag3 = graphEL.isSwitchDefaultLine(startNode, node);
            if (flag3) {
                switchDefaultList.add(node);
            }
        }
        if (switchDefaultList.size() > 1) {
            throw new LiteFlowELException("switch组件不能有多个default节点！");
        } else if (switchDefaultList.size() == 1 && switchToList.isEmpty()) {
            throw new LiteFlowELException("switch组件未设置switch to节点！");
        }
        List<ELWrapper> switchToELList = new ArrayList<>();
        List<ELWrapper> switchDefaultELList = new ArrayList<>();
        if (CollUtil.isNotEmpty(switchToList)) {
            for (Node node : switchToList) {
                NodeProperties properties = node.getProperties();
                ThenELWrapper nodeELWrapper = ELBus.then();
                setPropertiesToELWrapper(nodeELWrapper, properties);
                // EdgeProperties edgeProperties = graphEL.getEdgeProp(startNode, node);
                // if(edgeProperties != null) {
                //     if (StrUtil.isNotBlank(edgeProperties.getId())) {
                //         nodeELWrapper.id(edgeProperties.getId());
                //     }
                //     if (StrUtil.isNotBlank(edgeProperties.getTag())) {
                //         nodeELWrapper.tag(edgeProperties.getTag());
                //     }
                // }
                parseSingleFlow(nodeELWrapper, graphEL, node, null);
                switchToELList.add(nodeELWrapper);
            }
        }
        if (CollUtil.isNotEmpty(switchDefaultList)) {
            Node node = switchDefaultList.get(0);
            NodeProperties properties = node.getProperties();
            ThenELWrapper nodeELWrapper = ELBus.then();

            setPropertiesToELWrapper(nodeELWrapper, properties);
            parseSingleFlow(nodeELWrapper, graphEL, node, null);
            switchDefaultELList.add(nodeELWrapper);
        }

        if (CollUtil.isNotEmpty(switchToELList) || CollUtil.isNotEmpty(switchDefaultELList)) {
            NodeProperties properties = startNode.getProperties();
            if (CollUtil.isNotEmpty(switchDefaultELList)) {
                properties.setCmpDefaultOptEL(switchDefaultELList.get(0));
            }
            properties.setCmpToEL(switchToELList);
            ELWrapper elWrapper = ELBusSwitch.NEW().node(properties).toELWrapper();
            FlowConvertELUtil.convert(wrapper, elWrapper);
        } else {
            flowThen(wrapper, startNode);
        }
        if (commonList.size() == 1) {// 单起点
            parseSingleFlow(wrapper, graphEL, commonList.get(0), endNode);
        } else if (commonList.size() > 1) {// 多起点
            parseMultipleFlow(wrapper, graphEL, commonList, endNode);
        }
    }

    private static void setPropertiesToELWrapper(ThenELWrapper elWrapper, NodeProperties properties) {
        // elWrapper.id(properties.getComponentId());
        // elWrapper.tag(properties.getCmpTag());
        // String cmpDataName = properties.getCmpDataName();
        // // Map<String, ?> cmpData = properties.getCmpData();
        // // if (StrUtil.isNotEmpty(cmpDataName) && CollectionUtil.isNotEmpty(cmpData)) {
        // //     elWrapper.data(cmpDataName, cmpData);
        // // }
    }

    public static void flowIf(ELWrapper wrapper, Node startNode, Node endNode, LogicFlowGraphEL graphEL) throws LiteFlowELException {
        List<Node> nodeList = graphEL.getNextNode(startNode);
        List<Node> ifTrueList = new ArrayList<>();
        List<Node> ifFalseList = new ArrayList<>();
        List<Node> commonList = new ArrayList<>();
        for (Node node : nodeList) {
            boolean flag1 = graphEL.isCommonLine(startNode, node);
            if (flag1) {
                commonList.add(node);
            }
            boolean flag2 = graphEL.isIfTrueLine(startNode, node);
            if (flag2) {
                ifTrueList.add(node);
            }
            boolean flag3 = graphEL.isIfFalseLine(startNode, node);
            if (flag3) {
                ifFalseList.add(node);
            }
        }
        if (ifTrueList.size() > 1) {
            throw new LiteFlowELException("if组件不能有多个true节点！");
        } else if (ifFalseList.size() > 1) {
            throw new LiteFlowELException("if组件不能有多个false节点！");
        }
        ThenELWrapper trueELWrapper = null;
        ThenELWrapper falseELWrapper = null;
        if (CollUtil.isNotEmpty(ifTrueList)) {
            Node node = ifTrueList.get(0);
            NodeProperties properties = node.getProperties();
            trueELWrapper =  ELBus.then();
            setPropertiesToELWrapper(trueELWrapper, properties);

            parseSingleFlow(trueELWrapper, graphEL, node, null);
        }
        if (CollUtil.isNotEmpty(ifFalseList)) {
            Node node = ifFalseList.get(0);
            NodeProperties properties = node.getProperties();
            falseELWrapper =  ELBus.then();
            setPropertiesToELWrapper(falseELWrapper, properties);

            parseSingleFlow(falseELWrapper, graphEL, node, null);
        }

        if (trueELWrapper != null || falseELWrapper != null) {
            NodeProperties properties = startNode.getProperties();
            if (trueELWrapper != null) {
                properties.setCmpTrueOptEL(trueELWrapper);
            }
            if (falseELWrapper != null) {
                properties.setCmpFalseOptEL(falseELWrapper);
            }
            ELWrapper elWrapper = ELBusIf.NEW().node(properties).toELWrapper();
            FlowConvertELUtil.convert(wrapper, elWrapper);
        } else {
            flowThen(wrapper, startNode);
        }
        if (commonList.size() == 1) {// 单起点
            parseSingleFlow(wrapper, graphEL, commonList.get(0), endNode);
        } else if (commonList.size() > 1) {// 多起点
            parseMultipleFlow(wrapper, graphEL, commonList, endNode);
        }
    }

    public static void flowFor(ELWrapper wrapper, Node startNode, Node endNode, LogicFlowGraphEL graphEL) throws LiteFlowELException {
        List<Node> nodeList = graphEL.getNextNode(startNode);
        List<Node> doList = new ArrayList<>();
        List<Node> breakList = new ArrayList<>();
        List<Node> commonList = new ArrayList<>();
        for (Node node : nodeList) {
            boolean flag1 = graphEL.isCommonLine(startNode, node);
            if (flag1) {
                commonList.add(node);
            }
            boolean flag2 = graphEL.isForLine(startNode, node);
            if (flag2) {
                doList.add(node);
            }
            boolean flag3 = graphEL.isBreakLine(startNode, node);
            if (flag3) {
                breakList.add(node);
            }
        }
        if (doList.size() > 1) {
            throw new LiteFlowELException("for组件不能有多个do路径！");
        } else if (breakList.size() > 1) {
            throw new LiteFlowELException("for组件不能有多个break路径！");
        }
        ThenELWrapper doELWrapper = null;
        ELWrapper breakELWrapper = null;
        if (CollUtil.isNotEmpty(doList)) {
            doELWrapper = ELBus.then();
            parseSingleFlow(doELWrapper, graphEL, doList.get(0), null);
        }
        if (CollUtil.isNotEmpty(breakList)) {
//            breakELWrapper = ELBus.then();
//            parseSingleFlow(breakELWrapper, graphEL, breakList.get(0), null);
            breakELWrapper = ELBusBreak.NEW().nodeEL().node(breakList.get(0).getProperties()).toELWrapper();
        }

        if (doELWrapper != null || breakELWrapper != null) {
            NodeProperties properties = startNode.getProperties();
            if (doELWrapper != null) {
                properties.setCmpDoOptEL(doELWrapper);
            }
            if (breakELWrapper != null) {
                properties.setCmpBreakOptEL(breakELWrapper);
            }
            ELWrapper elWrapper = ELBusFor.node(properties);
            FlowConvertELUtil.convert(wrapper, elWrapper);
        } else {
            flowThen(wrapper, startNode);
        }
        if (commonList.size() == 1) {// 单起点
            parseSingleFlow(wrapper, graphEL, commonList.get(0), endNode);
        } else if (commonList.size() > 1) {// 多起点
            parseMultipleFlow(wrapper, graphEL, commonList, endNode);
        }
    }

    public static void flowWhile(ELWrapper wrapper, Node startNode, Node endNode, LogicFlowGraphEL graphEL) throws LiteFlowELException {
        List<Node> nodeList = graphEL.getNextNode(startNode);
        List<Node> doList = new ArrayList<>();
        List<Node> breakList = new ArrayList<>();
        List<Node> commonList = new ArrayList<>();
        for (Node node : nodeList) {
            boolean flag1 = graphEL.isCommonLine(startNode, node);
            if (flag1) {
                commonList.add(node);
            }
            boolean flag2 = graphEL.isWhileLine(startNode, node);
            if (flag2) {
                doList.add(node);
            }
            boolean flag3 = graphEL.isBreakLine(startNode, node);
            if (flag3) {
                breakList.add(node);
            }
        }
        if (doList.size() > 1) {
            throw new LiteFlowELException("while组件不能有多个do路径！");
        } else if (breakList.size() > 1) {
            throw new LiteFlowELException("while组件不能有多个break路径！");
        }
        ThenELWrapper doELWrapper = null;
        ELWrapper breakELWrapper = null;
        if (CollUtil.isNotEmpty(doList)) {
            doELWrapper = ELBus.then();
            parseSingleFlow(doELWrapper, graphEL, doList.get(0), null);
        }
        if (CollUtil.isNotEmpty(breakList)) {
            // breakELWrapper = ELBus.then();
            // parseSingleFlow(breakELWrapper, graphEL, breakList.get(0), null);
            breakELWrapper = ELBusBreak.NEW().nodeEL().node(breakList.get(0).getProperties()).toELWrapper();
        }

        if (doELWrapper != null || breakELWrapper != null) {
            NodeProperties properties = startNode.getProperties();
            if (doELWrapper != null) {
                properties.setCmpDoOptEL(doELWrapper);
            }
            if (breakELWrapper != null) {
                properties.setCmpBreakOptEL(breakELWrapper);
            }
            ELWrapper elWrapper = ELBusWhile.node(properties);
            FlowConvertELUtil.convert(wrapper, elWrapper);
        } else {
            flowThen(wrapper, startNode);
        }
        if (commonList.size() == 1) {// 单起点
            parseSingleFlow(wrapper, graphEL, commonList.get(0), endNode);
        } else if (commonList.size() > 1) {// 多起点
            parseMultipleFlow(wrapper, graphEL, commonList, endNode);
        }
    }

    public static void flowIterator(ELWrapper wrapper, Node startNode, Node endNode, LogicFlowGraphEL graphEL) throws LiteFlowELException {
        List<Node> nodeList = graphEL.getNextNode(startNode);
        List<Node> doList = new ArrayList<>();
        List<Node> commonList = new ArrayList<>();
        for (Node node : nodeList) {
            boolean flag1 = graphEL.isCommonLine(startNode, node);
            if (flag1) {
                commonList.add(node);
            }
            boolean flag2 = graphEL.isIteratorLine(startNode, node);
            if (flag2) {
                doList.add(node);
            }
        }
        if (doList.size() > 1) {
            throw new LiteFlowELException("iterator组件不能有多个do路径！");
        }
        ThenELWrapper doELWrapper = null;
        if (CollUtil.isNotEmpty(doList)) {
            doELWrapper = ELBus.then();
            parseSingleFlow(doELWrapper, graphEL, doList.get(0), null);
        }

        if (doELWrapper != null) {
            NodeProperties properties = startNode.getProperties();
            properties.setCmpDoOptEL(doELWrapper);
            ELWrapper elWrapper = ELBusIterator.node(properties);
            FlowConvertELUtil.convert(wrapper, elWrapper);
        } else {
            flowThen(wrapper, startNode);
        }
        if (commonList.size() == 1) {// 单起点
            parseSingleFlow(wrapper, graphEL, commonList.get(0), endNode);
        } else if (commonList.size() > 1) {// 多起点
            parseMultipleFlow(wrapper, graphEL, commonList, endNode);
        }
    }

    // 分叉节点处理
    public static void flowFork(ELWrapper wrapper, Node startNode, Node endNode, LogicFlowGraphEL graphEL) throws LiteFlowELException {
        flowThen(wrapper, startNode);
        Node joinNode = graphEL.getJoinNode(startNode);
        if (joinNode != null && graphEL.isMultipleJoin(startNode, joinNode)) {// 路径聚合
            parseMultipleFlow(wrapper, graphEL, graphEL.getNextNode(startNode), joinNode);
        } else if (joinNode == null && graphEL.isXNode(graphEL.getNextNode(startNode), null)) {
            Set<List<Node>> startNodeGroupList = graphEL.isMultipleStartAndXNode(startNode, endNode);
            if (CollUtil.isNotEmpty(startNodeGroupList)) {
                WhenELWrapper whenELWrapper = ELBus.when();
                for (List<Node> nodeList : startNodeGroupList) {
                    if (nodeList.size() == 1) {
                        ThenELWrapper thenELWrapper = ELBus.then();
                        parseSingleFlow(thenELWrapper, graphEL, nodeList.get(0), endNode);
                        whenELWrapper.when(thenELWrapper);
                    } else {
                        ThenELWrapper thenELWrapper = ELBus.then();
                        parseMultipleFlow(thenELWrapper, graphEL, nodeList, endNode);
                        whenELWrapper.when(thenELWrapper);
                    }
                }
                graphEL.setGroupNodeProp(startNode, whenELWrapper);
                FlowConvertELUtil.convert(wrapper, whenELWrapper);
            } else {
                flowWhen(wrapper, startNode, joinNode, graphEL);
                if (joinNode != null && joinNode != endNode) {
                    parseSingleFlow(wrapper, graphEL, joinNode, endNode);
                }
            }
        } else {
            flowWhen(wrapper, startNode, joinNode, graphEL);
            if (joinNode != null && joinNode != endNode) {
                parseSingleFlow(wrapper, graphEL, joinNode, endNode);
            }
        }
    }

    public static void flowMultiple(ELWrapper wrapper, List<Node> startNodeList, Node endNode, LogicFlowGraphEL graphEL) throws LiteFlowELException {
        WhenELWrapper whenELWrapper = ELBus.when();
        boolean flag = graphEL.isXNode(startNodeList, endNode);
        if (flag) {
            Set<List<Node>> startNodeGroupList = graphEL.startNodeGroupList(startNodeList, endNode);
            for (List<Node> nodeList : startNodeGroupList) {
                if (nodeList.size() == 1) {
                    ThenELWrapper thenELWrapper = ELBus.then();
                    parseSingleFlow(thenELWrapper, graphEL, nodeList.get(0), endNode);
                    whenELWrapper.when(thenELWrapper);
                } else {
                    ThenELWrapper thenELWrapper = ELBus.then();
                    parseMultipleFlow(thenELWrapper, graphEL, nodeList, endNode);
                    whenELWrapper.when(thenELWrapper);
                }
            }
        } else {
            for (Node startNode : startNodeList) {
                if (graphEL.getNextNode(startNode).contains(endNode)) {
                    whenELWrapper.when(nodeToEL(startNode));
                } else {
                    ThenELWrapper thenELWrapper = ELBus.then();
                    parseSingleFlow(thenELWrapper, graphEL, startNode, endNode);
                    whenELWrapper.when(thenELWrapper);
                }
            }
        }
        graphEL.setGroupNodeProp(startNodeList, whenELWrapper);
        FlowConvertELUtil.convert(wrapper, whenELWrapper);
    }

    public static void flowWhen(ELWrapper wrapper, Node startNode, Node endNode, LogicFlowGraphEL graphEL) throws LiteFlowELException {
        List<Node> nextNodeList = graphEL.getNextNode(startNode);
        WhenELWrapper whenELWrapper = ELBus.when();
        for (Node nextNode : nextNodeList) {
            if (graphEL.isLastNode(nextNode) || graphEL.getNextNode(nextNode).contains(endNode)) {
                whenELWrapper.when(nodeToEL(nextNode));
            } else {
                ThenELWrapper thenELWrapper = ELBus.then();
                parseSingleFlow(thenELWrapper, graphEL, nextNode, endNode);
                whenELWrapper.when(thenELWrapper);
            }
        }
        graphEL.setGroupNodeProp(startNode, whenELWrapper);
        FlowConvertELUtil.convert(wrapper, whenELWrapper);
    }

    // 串行处理
    public static void flowThen(ELWrapper wrapper, Node startNode) throws LiteFlowELException {
        FlowConvertELUtil.convert(wrapper, nodeToEL(startNode));
    }

    // 前置组件处理
    public static void preELWrapper(ThenELWrapper thenELWrapper, LogicFlowGraphEL graphEL) {
        Object[] preArray = nodeToEL(graphEL.getPreList());
        if (preArray != null) {
            thenELWrapper.pre(preArray);
        }
    }

    // 后置组件处理
    public static void finallyELWrapper(ThenELWrapper thenELWrapper, LogicFlowGraphEL graphEL) {
        Object[] finallyArray = nodeToEL(graphEL.getFinallyList());
        if (finallyArray != null) {
            thenELWrapper.finallyOpt(finallyArray);
        }
    }


    public static Object nodeToEL(Node node) throws LiteFlowELException {
        if (node == null) {
            return null;
        }
        return getELWrapper(node);
    }

    public static Object[] nodeToEL(List<Node> nodeList) {
        if (nodeList == null) {
            return null;
        }
        return nodeList.stream().map(m -> {
            try {
                return getELWrapper(m);
            } catch (LiteFlowELException e) {
                throw new RuntimeException(e);
            }
        }).filter(Objects::nonNull).toArray();
    }

    public static Object getELWrapper(Node node) throws LiteFlowELException {
        NodeProperties nodeInfoWrapper = node.getProperties();
        return NodeInfoToELUtil.buildELWrapper(nodeInfoWrapper, true);
    }

}
